package user;

import java.security.*;
import java.sql.*;
import java.util.*;

import common.dal.*;
import common.dal.SQLDataColumn.Constraints;
import common.exceptions.ExistingEntityException;
import common.interfaces.IUserRegisteryManager;

/**
 * A database implementation of {@link IUserRegisteryManager}
 * 
 * @author Gal Lalouche
 */
public class DBPasswordManager extends AbstractDAL<User, String> implements IUserRegisteryManager {
	/**
	 * The table's name in the DB
	 */
	public static final String		TABLE_NAME		= "login";
	/**
	 * The primary key used by the table
	 */
	public static final String		PRIMARY_KEY		= "user";
	
	private static final int		ADMIN_DEFAULT	= 0;
	private static final String		PASS			= "password";
	private static final String		NAME			= "name";
	private static final String		IS_ADMIN		= "isAdmin";
	private final byte[]			adminPass;
	
	private static MessageDigest	digester;
	
	{
		try {
			digester = MessageDigest.getInstance("SHA-1");
		} catch (NoSuchAlgorithmException e) {
			throw new AssertionError(e);
		}
	}
	
	@Deprecated
	DBPasswordManager(String adminPassword) {
		super(null);
		adminPass = hashString(adminPassword);
	}
	
	/**
	 * @param t The transaction all queries from this DB will be under
	 */
	public DBPasswordManager(Transaction t) {
		super(t);
		adminPass = null;
	}
	
	private static byte[] hashString(String password) {
		byte[] result = digester.digest(password.getBytes());
		return result;
	}
	
	@SuppressWarnings("deprecation")
	@Override
	protected User generateFromResultSet(ResultSet result) throws SQLException {
		if (result.getInt(IS_ADMIN) == ADMIN_DEFAULT) {
			return new User(result.getString(PRIMARY_KEY), result.getString(NAME));
		} else {
			return new Administrator(result.getString(PRIMARY_KEY), result.getString(NAME));
		}
	}
	
	@Override
	protected SQLDataColumn[] getTableColumns() {
		return new SQLDataColumn[] {
				new SQLDataColumn(PRIMARY_KEY, SQLDataType.VARCHAR_50, Constraints.PRIMARY_KEY),
				new SQLDataColumn(NAME, SQLDataType.TINYTEXT, Constraints.NOT_NULL),
				new SQLDataColumn(PASS, SQLDataType.TINYBLOB, Constraints.NOT_NULL),
				new SQLDataColumn(IS_ADMIN, SQLDataType.INTEGER,
						EnumSet.of(Constraints.NOT_NULL, Constraints.DEFAULT), ADMIN_DEFAULT)};
	}
	
	@Override
	protected String getTableName() {
		return TABLE_NAME;
	}
	
	@Override
	protected String getPrimaryColumnName() {
		return PRIMARY_KEY;
	}
	
	@Override
	public boolean isPasswordOf(String username, String password) throws SQLException {
		if (username == null) {
			throw new IllegalArgumentException("user is null");
		}
		if (password == null) {
			throw new IllegalArgumentException("password is null");
		}
		try (ResultSet pass = getInternal(username)) {
			if (pass.first() == false) {
				return false;
			}
			byte[] result = pass.getBytes(PASS);
			byte[] hashedPass = hashString(password);
			assert pass.next() == false; // only one result, cause user is key
			return Arrays.equals(result, hashedPass);
		}
	}
	
	@Override
	public void registerUser(String requestedUsername, String name, String requestedPassword)
			throws ExistingEntityException, SQLException {
		if (exists(requestedUsername)) {
			throw new ExistingEntityException(requestedUsername);
		}
		
		insertInto(new Object[] {requestedUsername, name, hashString(requestedPassword)});
	}
	
	@Override
	@Deprecated
	public void registerAdmin(String requestedUsername, String name, String requestedPassword)
			throws ExistingEntityException, SQLException {
		if (requestedUsername == null) {
			throw new IllegalArgumentException("requestedUsername is null");
		}
		if (name == null) {
			throw new IllegalArgumentException("name is null");
		}
		if (requestedPassword == null) {
			throw new IllegalArgumentException("requestedPassword is null");
		}
		
		if (exists(requestedUsername)) {
			throw new ExistingEntityException(requestedUsername);
		}
		insertInto(new Object[] {requestedUsername, name, hashString(requestedPassword), ADMIN_DEFAULT + 1});
	}
	
	@Override
	public boolean unregisterUser(String username) throws SQLException {
		return delete(username);
	}
	
	@Override
	@Deprecated
	public boolean checkAdminPassword(String password) {
		if (password == null) {
			throw new IllegalArgumentException("password is null");
		}
		
		return Arrays.equals(hashString(password), adminPass);
	}
}
